home *** CD-ROM | disk | FTP | other *** search
- /*
- * Does this restore volume on suspend/exit? (deactivate sufficient?)
- *
- * Gnome -- a Macintosh metronome.
- *
- * This is a TransSkel application (requires TransSkel 2.0 or higher).
- * The project should include Gnome.c, ControlBar.Lib, TransSkel.lib,
- * and MacTraps.
- *
- * This application is public domain, and is written by:
- *
- * Paul DuBois
- * Wisconsin Regional Primate Research Center
- * 1220 Capitol Court
- * Madison, WI 53715-1299 USA
- *
- * Internet: dubois@primate.wisc.edu
- *
- * 13 Feb 88 Release 1.00
- *
- * 02 Dec 93 Release 1.01
- * - Updated for TransSkel 3.04. Made multitasking aware; doesn't beat
- * while suspended. On deactivate/suspend I only make the scrolls inactive.
- * I don't hide and outline them because that looks ugly.
- *
- * 09 Dec 93 Release 1.02
- * - Updated for TransSkel 3.05.
- *
- * 21 Dec 93 Release 1.03
- * - Use color grafport when available.
- * 29 Jan 94
- * - Add outline around Start button, make Return/Enter synonyms for clicking
- * it, and make Escape and Command-period synonyms for clicking Stop button.
- * Command-period is handled in the menu hook.
- *
- * 21 Feb 94 Release 1.04
- * - Updated for TransSkel 3.11, ControlBar 1.05.
- * - Position document window and About alert using TransSkel convenience
- * routines.
- */
-
- # include <Sound.h>
-
- # include "TransSkel.h"
-
- # include "Gnome.h"
- # include "ControlBar.h"
-
- # define normalHilite 0
- # define dimHilite 255
-
- # define returnKey 13
- # define enterKey 3
- # define escapeKey 27
-
- # define nBars 7 /* number of scroll bars */
-
- # define sqSize 15 /* half-size of auditory metronome square */
- # define sqH 365 /* horizontal middle of square */
- # define sqV 110 /* vertical middle of square */
-
-
- typedef void (*VoidProcPtr) (void);
-
-
- static void ToneOn (void);
- static void ToneOff (void);
- static void TickOn (void);
- static void TickOff (void);
- static VoidProcPtr SoundOn = TickOn;
- static VoidProcPtr SoundOff = TickOff;
- static void CheckBeat (void);
-
- static pascal void BarCheckBeat (ControlHandle bar);
- static pascal void SetInitBar (ControlHandle bar);
- static pascal void SetVolumeBar (ControlHandle bar);
-
- static WindowPtr gnomeWind;
-
- static ControlHandle startBtn;
- static ControlHandle stopBtn;
-
- static ControlHandle auditoryBox;
- static ControlHandle visualBox;
-
- static ControlHandle tickBox;
- static ControlHandle toneBox;
-
- static ControlHandle initRateBar;
- static ControlHandle finalRateBar;
- static ControlHandle seriesLenBar;
- static ControlHandle pitchBar;
- static ControlHandle rateChgBar;
- static ControlHandle countDownBar;
- static ControlHandle volumeBar;
-
- static BarInfo bar[nBars] =
- {
- { &initRateBar, "\pInitial Rate = ", 0, 10, 25, 140,
- 1, 400, 60, { 0, "\p" }, SetInitBar, BarCheckBeat },
- { &finalRateBar, "\pFinal Rate = ", 0, 10, 65, 140,
- 1, 400, 60, { 0, "\p" }, nil, BarCheckBeat },
- { &rateChgBar, "\pRate Change = ", 0, 10, 105, 140,
- 0, 100, 0, { 0, "\p" }, nil, BarCheckBeat },
- { &seriesLenBar, "\pBeats/Rate = ", 0, 10, 145, 140,
- 1, 500, 1, { 0, "\p" }, nil, BarCheckBeat },
- { &countDownBar, "\pCountdown Beats = ", 0, 10, 185, 140,
- 0, 32, 0, { 0, "\p" }, nil, BarCheckBeat },
- { &pitchBar, "\pPitch = ", 0, 170, 65, 140,
- 100, 2000, 880, { 0, "\p" }, nil, BarCheckBeat },
- { &volumeBar, "\pVolume = ", 0, 170, 105, 140,
- 0, 31, 0, { 0, "\p" }, SetVolumeBar, BarCheckBeat }
- };
-
-
- static char swBuf[sizeof (int) + 2 * sizeof (Tone)];
- static SWSynthPtr sw = (SWSynthPtr) &swBuf;
- static char ffBuf[6+370+370];
- static FFSynthPtr ff = (FFSynthPtr) &ffBuf;
-
- static short on = 0; /* non-zero if beating */
- static short visual = 1; /* non-zero if visual metronome enabled */
- static short auditory = 1; /* non-zero if auditory metronome enabled */
- static long beatStart; /* when to start next beat */
- static long beatStop; /* when to stop next beat */
- static short curRate; /* current metronome rate */
- static short nextRate; /* metronome rate after next beat */
- static short beatNum; /* beat number in current series */
- static short inCountDown; /* non-zero if in countdown series */
- static ValInfo curRateInfo = { -1, "\pXXXXX" };
- static ValInfo beatNumInfo = { -1, "\pXXXXX" };
-
- static short origVolume;
-
-
- /* -------------------------------------------------------------------- */
- /* Display routines */
- /* -------------------------------------------------------------------- */
-
- /*
- * These functions use ControlBar's UpdateBarNum() function (even though
- * they're not associated with a scroll bar) to minimize flicker.
- */
-
- static void
- DrawRate (Boolean drawAll)
- {
- StringPtr s = "\pCurrent rate = ";
- short h;
-
- if (drawAll) /* draw title too */
- {
- MoveTo (270, 163);
- DrawString (s);
- }
- h = 270 + StringWidth (s);
- UpdateBarNum (h, 163, on ? curRate : 0, &curRateInfo, drawAll);
- }
-
-
- static void
- DrawBeat (Boolean drawAll)
- {
- StringPtr s = "\pCurrent beat = ";
- short h;
-
- if (drawAll) /* draw title too */
- {
- MoveTo (270, 183);
- DrawString (s);
- }
- h = 270 + StringWidth (s);
- UpdateBarNum (h, 183, on ? beatNum : 0, &beatNumInfo, drawAll);
- }
-
-
- /* -------------------------------------------------------------------- */
- /* Metronome beat generation control routines */
- /* CheckBeat () see if time for next beat */
- /* StartBeat () start next beat */
- /* StopBeat () stop current beat */
- /* NextBeatTime () determine next beat time */
- /* StartGnome () turn on metronome */
- /* StopGnome () turn off metronome */
- /* -------------------------------------------------------------------- */
-
-
- /*
- * The Control Manager seems to set the pen to gray while it's tracking
- * a mouse-down in a scroll bar thumb. This function can be called during
- * tracking, it's necessary to save, set, and restore the pen to avoid
- * drawing in gray.
- */
-
- static void
- StartBeat (void)
- {
- PenState ps;
- Rect r;
-
- ++beatNum;
- DrawBeat (false);
- if (visual)
- {
- SetRect (&r, sqH-sqSize, sqV-sqSize, sqH+sqSize, sqV+sqSize);
- GetPenState (&ps);
- PenNormal ();
- PaintRect (&r);
- SetPenState (&ps);
- }
- if (auditory)
- {
- (*SoundOn)();
- }
- }
-
-
- static void
- StopBeat (void)
- {
- Rect r;
-
- SetRect (&r, sqH-sqSize, sqV-sqSize, sqH+sqSize, sqV+sqSize);
- EraseRect (&r);
- (*SoundOff)();
- }
-
-
- static void
- ToneOn (void)
- {
- sw->triplets[0].count = 783360L / GetCtlValue (pitchBar);
- sw->triplets[0].duration = 4;
- StartSound (sw, sizeof (swBuf), nil);
- }
-
-
- static void
- ToneOff (void)
- {
- }
-
-
- static void
- TickOn (void)
- {
- StartSound (ff, sizeof (ffBuf), nil);
- }
-
-
- static void
- TickOff (void)
- {
- StopSound ();
- }
-
-
- static void
- StartGnome (void)
- {
- on = 1;
- nextRate = GetCtlValue (initRateBar);
- DrawRate (false);
- inCountDown = (GetCtlValue (countDownBar) != 0);
- beatNum = 0;
- beatStart = TickCount (); /* first beat should occur immediately */
- }
-
-
- static void
- StopGnome (void)
- {
- StopBeat ();
- on = 0;
- DrawRate (false);
- DrawBeat (false);
- }
-
-
- /*
- * This algorithm handles all combinations of parameters.
- * There is no special provision for cases such as constant metronomes
- * (init rate = final rate) or series lengths of 1. The algorithm
- * could be optimized for such, at the cost of greater complexity.
- *
- * There are 60 ticks/second, 3600 ticks/minute. Since rate is measured
- * as beats/minute, number of ticks between beats is 3600/curRate. Not
- * particularly accurate at high rates. Max achievable rate is 1800
- * ticks a minute.
- */
-
- static void
- NextBeatTime (void)
- {
- short initRate;
- short finalRate;
- short rateChange;
-
- if (curRate != nextRate)
- {
- curRate = nextRate;
- DrawRate (false);
- }
- beatStop = beatStart;
- beatStart = TickCount () + (3600L / curRate);
- beatStop = (beatStart + beatStop) / 2;
- if (inCountDown)
- {
- rateChange = 0;
- if (beatNum >= GetCtlValue (countDownBar))
- {
- inCountDown = 0; /* done with countdown; start */
- beatNum = 0; /* first series */
- }
- }
- else
- {
- if (beatNum < GetCtlValue (seriesLenBar))
- rateChange = 0; /* not done with series */
- else
- {
- rateChange = GetCtlValue (rateChgBar);
- beatNum = 0;
- }
- }
-
- /*
- * Set up rate for next series
- */
-
- initRate = GetCtlValue (initRateBar);
- finalRate = GetCtlValue (finalRateBar);
- if (initRate < finalRate) /* increasing rate */
- {
- nextRate += rateChange;
- if (nextRate > finalRate) /* cycle back to initial rate */
- nextRate = initRate;
- }
- else /* decreasing or constant rate */
- {
- nextRate -= rateChange;
- if (nextRate < finalRate) /* cycle back to initial rate */
- nextRate = initRate;
- }
- }
-
-
- static void
- CheckBeat (void)
- {
- long t;
-
- if (!on || !SkelQuery (skelQInForeground))
- return; /* metronome off or suspended */
- if ((t = TickCount ()) >= beatStart) /* time for next beat */
- {
- StartBeat (); /* start beat */
- NextBeatTime (); /* determine time of next beat, */
- /* reset beatNum s'il faut */
- }
- else if (t >= beatStop)
- StopBeat ();
- }
-
-
- /* -------------------------------------------------------------------- */
- /* Scroll bar and button handler routines */
- /* -------------------------------------------------------------------- */
-
-
- /*
- * Set sound volume. The amplitude of the wave modulates within
- * each SetSoundVol() level to attain variation between
- * 0 and 31, rather than between 0 and 7.
- */
-
- static void
- SetSound (short vol)
- {
- SetSoundVol (vol/4);
- if (vol > 0)
- {
- switch (vol % 4)
- {
- case 0: vol = 180; break;
- case 1: vol = 205; break;
- case 2: vol = 230; break;
- case 3: vol = 255; break;
- }
- }
- sw->triplets[0].amplitude = vol;
- }
-
- /*
- * Make rate follow initial rate if mouse down in initRateBar
- */
-
- static pascal void
- SetInitBar (ControlHandle bar)
- {
- nextRate = GetCtlValue (bar);
- DrawRate (false);
- }
-
-
- static pascal void
- SetVolumeBar (ControlHandle bar)
- {
- SetSound (GetCtlValue (bar));
- }
-
-
- /*
- * This is the idle function for all the bars. It simply calls CheckBeat()
- * to see whether a new beat needs to be generated during bar mouse tracking.
- * It's shared among them and the particular bar is irrelevant.
- */
-
- static pascal void
- BarCheckBeat (ControlHandle bar)
- {
- CheckBeat ();
- }
-
-
- /*
- * Check for next beat while mouse down in button
- */
-
- static pascal void
- TrackButton (ControlHandle ctrl, short partCode)
- {
- CheckBeat ();
- }
-
-
- /* -------------------------------------------------------------------- */
- /* Window handler routines */
- /* -------------------------------------------------------------------- */
-
-
- /*
- Process mouse hit in control.
- */
-
- static pascal void
- Mouse (Point pt, long t, short mods)
- {
- ControlHandle ctrl;
- short trackPart;
-
- if ((trackPart = FindControl (pt, gnomeWind, &ctrl)) != 0)
- {
- if (trackPart == inButton) /* start or stop button */
- {
- if (TrackControl (ctrl, pt, TrackButton) != 0)
- {
- if (ctrl == startBtn)
- StartGnome ();
- else
- StopGnome ();
- }
- }
- else if (trackPart == inCheckBox)
- {
- if (TrackControl (ctrl, pt, TrackButton) != 0)
- {
- if (ctrl == tickBox)
- {
- SoundOn = TickOn;
- SoundOff = TickOff;
- SetCtlValue (tickBox, 1);
- SetCtlValue (toneBox, 0);
- HiliteControl (pitchBar, dimHilite);
- }
- else if (ctrl == toneBox)
- {
- SoundOn = ToneOn;
- SoundOff = ToneOff;
- SetCtlValue (tickBox, 0);
- SetCtlValue (toneBox, 1);
- HiliteControl (pitchBar, normalHilite);
- }
- else if (ctrl == auditoryBox)
- {
- SetCtlValue (ctrl, auditory = !GetCtlValue (ctrl));
- }
- else
- {
- SetCtlValue (ctrl, visual = !GetCtlValue (ctrl));
- }
- }
- }
- else
- TrackBar (ctrl, pt, trackPart);
- }
- }
-
-
- static pascal void
- Key (short c, short code, short mods)
- {
- if (c == returnKey || c == enterKey)
- {
- SkelFlashButton (startBtn);
- StartGnome ();
- }
- else if (c == escapeKey)
- {
- SkelFlashButton (stopBtn);
- StopGnome ();
- }
- }
-
-
- static pascal void
- Update (Boolean resized)
- {
- short i;
-
- MoveTo (202, 20);
- DrawString ("\pSound Type");
- DrawControls (gnomeWind);
- SkelDrawButtonOutline (startBtn);
- for (i = 0; i < nBars; i++)
- {
- DrawBarTitle (*bar[i].bBar);
- DrawBarValue (*bar[i].bBar, true);
- }
- DrawRate (true);
- DrawBeat (true);
- }
-
-
- static pascal void
- Activate (Boolean active)
- {
- ControlHandle ctrl;
- short i;
- short hilite = (active ? normalHilite : dimHilite);
-
- if (!active) /* deactivated */
- {
- StopBeat ();
- SetSoundVol (origVolume);
- }
- else
- {
- SetSound (GetCtlValue (volumeBar));
- }
-
- HiliteControl (startBtn, hilite);
- SkelDrawButtonOutline (startBtn);
- HiliteControl (stopBtn, hilite);
- HiliteControl (auditoryBox, hilite);
- HiliteControl (visualBox, hilite);
- HiliteControl (tickBox, hilite);
- HiliteControl (toneBox, hilite);
- for (i = 0; i < nBars; i++)
- {
- ctrl =*bar[i].bBar;
- if (ctrl == pitchBar && active)
- {
- if (GetCtlValue (toneBox) != 0)
- HiliteControl (ctrl, normalHilite);
- else
- HiliteControl (ctrl, dimHilite);
-
- }
- else
- HiliteControl (ctrl, hilite);
- DrawBarValue (*bar[i].bBar, true);
- }
- }
-
-
- static pascal void
- Clobber (void)
- {
- HideWindow (gnomeWind);
- DisposeWindow (gnomeWind);
- }
-
-
- /* -------------------------------------------------------------------- */
- /* Menu handlers */
- /* -------------------------------------------------------------------- */
-
-
- /*
- * Handle "About Gnome..." selection from Apple menu.
- */
-
- static pascal void
- DoAppleMenu (short item)
- {
- (void) SkelAlert (aboutAlrtNum, SkelDlogFilter (nil, true),
- skelPositionOnParentDevice);
- SkelRmveDlogFilter ();
- }
-
-
- static pascal void
- DoFileMenu (short item)
- {
- StopBeat ();
- SkelStopEventLoop (); /* only one item -- Quit */
- }
-
-
- /* -------------------------------------------------------------------- */
- /* Application idle-time function */
- /* -------------------------------------------------------------------- */
-
-
- static pascal void
- Idle (void)
- {
- CheckBeat ();
- }
-
-
- /* -------------------------------------------------------------------- */
- /* Menu hook */
- /* -------------------------------------------------------------------- */
-
-
- /*
- * Command-period is mapped to the Stop button, but it has to be caught
- * in a menu hook function since otherwise it will be ignored. (Since
- * it's a command-key event, it's not passed to the Key() function.)
- */
-
- static pascal void
- MyMenuHook (void)
- {
- if (SkelCmdPeriod (SkelGetCurrentEvent ()))
- {
- SkelFlashButton (stopBtn);
- StopGnome ();
- }
- }
-
-
- /* -------------------------------------------------------------------- */
- /* Suspend/resume handler */
- /* -------------------------------------------------------------------- */
-
-
- static pascal void
- SuspendResume (Boolean inForeground)
- {
- SkelActivate (FrontWindow (), inForeground);
- }
-
-
- /* -------------------------------------------------------------------- */
- /* Initialization */
- /* -------------------------------------------------------------------- */
-
-
- static void
- MenuInit ()
- {
- MenuHandle m;
-
- SkelApple ("\pAbout Gnome\311", DoAppleMenu);
- m = NewMenu (1000, "\pFile");
- AppendMenu (m, "\pQuit/Q");
- (void) SkelMenu (m, DoFileMenu, nil, false, false);
- DrawMenuBar ();
- }
-
- static void
- WaveInit (void)
- {
- short i;
-
- sw->mode = swMode;
- /* triplet[0] set later */
- sw->triplets[1].count = 0;
- sw->triplets[1].amplitude = 0;
- sw->triplets[1].duration = 0;
-
- ff->mode = ffMode;
- ff->count = 0x10000; /* 1.0 in fixed notation */
- for (i = 6; i < sizeof (ffBuf); ++i)
- {
- ffBuf[i] = Random () & 255; /* 0 .. 255 */
- if (ffBuf[i] < 128)
- ffBuf[i] += 128;
- }
- }
-
-
- static void
- WindInit (void)
- {
- Rect r;
- short i;
-
- SetRect (&r, 0, 0, 415, 210);
- if (SkelQuery (skelQHasColorQD))
- {
- gnomeWind = NewCWindow (nil, &r, "\pGnome", false, noGrowDocProc,
- (WindowPtr) -1L, false, 0L);
- }
- else
- {
- gnomeWind = NewWindow (nil, &r, "\pGnome", false, noGrowDocProc,
- (WindowPtr) -1L, false, 0L);
- }
- SkelPositionWindow (gnomeWind, skelPositionOnMainDevice,
- FixRatio (1, 2), FixRatio (1, 5));
- (void) SkelWindow (gnomeWind,
- Mouse,
- Key,
- Update,
- Activate,
- nil,
- Clobber,
- nil,
- true);
-
- TextFont (0);
- TextSize (0);
-
- /*
- * Make controls for window.
- */
-
- SetRect (&r, 330, 15, 400, 35);
- startBtn = NewControl (gnomeWind, &r, "\pStart", true, 0, 0, 1,
- pushButProc, 0L);
- OffsetRect (&r, 0, 35);
- stopBtn = NewControl (gnomeWind, &r, "\pStop", true, 0, 0, 1,
- pushButProc, 0L);
-
- SetRect (&r, 183, 24, 243, 40);
- toneBox = NewControl (gnomeWind, &r, "\pTone", true,
- 0, 0, 1, radioButProc, 0L);
- OffsetRect (&r, 70, 0);
- tickBox = NewControl (gnomeWind, &r, "\pTick", true,
- 1, 0, 1, radioButProc, 0L);
-
- SetRect (&r, 168, 150, 268, 166);
- auditoryBox = NewControl (gnomeWind, &r, "\pAuditory", true,
- auditory, 0, 1, checkBoxProc, 0L);
- OffsetRect (&r, 0, 20);
- visualBox = NewControl (gnomeWind, &r, "\pVisual", true,
- visual, 0, 1, checkBoxProc, 0L);
-
- for (i = 0; i < nBars; i++)
- {
- MakeBar (&bar[i], gnomeWind);
- }
-
- GetSoundVol (&i);
- i *= 4;
- SetBarValue (volumeBar, i);
-
- HiliteControl (pitchBar, dimHilite);
-
- ShowWindow (gnomeWind); /* done initializing; make visible */
- }
-
-
- /* -------------------------------------------------------------------- */
-
-
- int
- main (void)
- {
- SkelInit ((SkelInitParamsPtr) nil);
- GetSoundVol (&origVolume);
- randSeed = TickCount ();
- MenuInit ();
- WaveInit ();
- WindInit ();
- SkelSetIdle (Idle);
- SkelSetSuspendResume (SuspendResume);
- SkelSetMenuHook (MyMenuHook);
- SkelEventLoop ();
- SetSoundVol (origVolume);
- SkelCleanup ();
- }